using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;
using CSimpleJSON;
using Newtonsoft.Json.Linq;
public class BaiduPanUploader {
    readonly string _accessToken;
    readonly HttpClient _httpClient = new HttpClient();

    public BaiduPanUploader(string accessToken) {
        _accessToken = accessToken;
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_accessToken}");
    }

    // 预上传：获取uploadid和分片大小
    async Task<(string uploadId, int chunkSize)> PreUpload(string localFilePath, string remoteDir) {
        var fileName = Path.GetFileName(localFilePath);
        var fileSize = new FileInfo(localFilePath).Length;

        var url = $"https://pan.baidu.com/rest/2.0/xpan/file?method=precreate&access_token={_accessToken}";
        var data = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("path", $"{remoteDir}/{fileName}"), new KeyValuePair<string, string>("size", fileSize.ToString()), new KeyValuePair<string, string>("isdir", "0"), new KeyValuePair<string, string>("autoinit", "1") });

        var response = await _httpClient.PostAsync(url, data);
        var result = JSON.Parse(await response.Content.ReadAsStringAsync());
        return (result["uploadid"].ToString(), (int)(result["block_list"].Count));
    }

    // 上传分片
    async Task<List<string>> UploadChunks(string localFilePath, string uploadId, int chunkSize) {
        var fileSize = new FileInfo(localFilePath).Length;
        var totalChunks = (int)Math.Ceiling((double)fileSize / chunkSize);
        var blockMd5s = new List<string>();

        using (var fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read)) {
            for (int i = 0; i < totalChunks; i++) {
                var buffer = new byte[chunkSize];
                var bytesRead = await fileStream.ReadAsync(buffer, 0, chunkSize);
                if (bytesRead == 0)
                    break;

                // 计算分片MD5（百度要求）
                using (var md5 = MD5.Create()) {
                    var hash = md5.ComputeHash(buffer, 0, bytesRead);
                    var blockMd5 = BitConverter.ToString(hash).Replace("-", "").ToLower();
                    blockMd5s.Add(blockMd5);
                }

                // 上传分片
                var url = $"https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?method=upload&access_token={_accessToken}&path=/&uploadid={uploadId}&partseq={i}";
                using (var content = new ByteArrayContent(buffer, 0, bytesRead)) {
                    var chunkResponse = await _httpClient.PostAsync(url, content);
                    if (!chunkResponse.IsSuccessStatusCode)
                        throw new Exception($"分片{i}上传失败：{await chunkResponse.Content.ReadAsStringAsync()}");
                }
            }
        }
        return blockMd5s;
    }

    // 合并分片，完成上传
    async Task<JObject> MergeChunks(string localFilePath, string remoteDir, string uploadId, List<string> blockMd5s) {
        var fileName = Path.GetFileName(localFilePath);
        var fileSize = new FileInfo(localFilePath).Length;

        var url = $"https://pan.baidu.com/rest/2.0/xpan/file?method=create&access_token={_accessToken}";
        var data = new FormUrlEncodedContent(new[] {
            new KeyValuePair<string, string>("path", $"{remoteDir}/{fileName}"),
            new KeyValuePair<string, string>("size", fileSize.ToString()),
            new KeyValuePair<string, string>("isdir", "0"),
            new KeyValuePair<string, string>("uploadid", uploadId),
            new KeyValuePair<string, string>("block_list", $"[\"{string.Join("\",\"", blockMd5s)}\"]")
        });

        var response = await _httpClient.PostAsync(url, data);
        return JObject.Parse(await response.Content.ReadAsStringAsync());
    }

    // 完整上传方法（对外调用）
    public async Task<JObject> UploadFile(string localFilePath, string remoteDir = "/") {
        if (!File.Exists(localFilePath))
            throw new FileNotFoundException("本地文件不存在", localFilePath);

        // 1. 预上传
        var (uploadId, chunkSize) = await PreUpload(localFilePath, remoteDir);
        // 2. 上传分片
        var blockMd5s = await UploadChunks(localFilePath, uploadId, chunkSize);
        // 3. 合并分片
        return await MergeChunks(localFilePath, remoteDir, uploadId, blockMd5s);
    }
}
